package spacewar2.clients;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.UUID;

import spacewar2.actions.DoNothingAction;
import spacewar2.actions.MoveAction;
import spacewar2.actions.MoveToObjectAction;
import spacewar2.actions.SpacewarAction;
import spacewar2.actions.SpacewarActionException;
import spacewar2.actions.SpacewarPurchaseEnum;
import spacewar2.objects.Beacon;
import spacewar2.objects.Ship;
import spacewar2.objects.SpacewarActionableObject;
import spacewar2.objects.SpacewarObject;
import spacewar2.powerups.SpacewarPowerup;
import spacewar2.shadows.Shadow;
import spacewar2.simulator.Toroidal2DPhysics;
import spacewar2.utilities.Position;

/**
 * Assigns each member of the team to collect a beacon.  If there 
 * are more beacons than team members, the remaining ones do nothing.
 *  
 * @author amy
 */
public class BeaconCollectorTeamClient extends TeamClient {
	/**
	 * Map of the beacon to which ship is aiming for it
	 */
	HashMap<Beacon, Ship> beaconToShipMap;
	HashMap<Ship, Beacon> shipToBeaconMap;

	/**
	 * Send each ship to a beacon
	 */
	public Map<UUID, SpacewarAction> getMovementStart(Toroidal2DPhysics space,
			Set<SpacewarActionableObject> actionableObjects) {
		HashMap<UUID, SpacewarAction> actions = new HashMap<UUID, SpacewarAction>();

		// loop through each ship
		for (SpacewarObject actionable :  actionableObjects) {
			if (actionable instanceof Ship) {
				Ship ship = (Ship) actionable;
				SpacewarAction current = ship.getCurrentAction();

				// does the ship have a beacon it is aiming for?
				if (current == null || current.isMovementFinished(space) || !shipToBeaconMap.containsKey(ship)) {
					Position currentPosition = ship.getPosition();
					Beacon beacon = pickNearestFreeBeacon(space, ship);

					SpacewarAction newAction = null;

					if (beacon == null) {
						// there is no beacon available so do nothing
						newAction = new DoNothingAction();
					} else {
						beaconToShipMap.put(beacon, ship);
						shipToBeaconMap.put(ship, beacon);
						Position newGoal = beacon.getPosition();
						newAction = new MoveToObjectAction(space, currentPosition, beacon);
					}
					actions.put(ship.getId(), newAction);
				} else {
					actions.put(ship.getId(), ship.getCurrentAction());
				}
			} else {
				// it is a base and Beacon collector doesn't do anything to bases
				actions.put(actionable.getId(), new DoNothingAction());
			}
		}

		return actions;
	}


	/**
	 * Find the nearest free beacon to this ship
	 * @param space
	 * @param ship
	 * @return
	 */
	private Beacon pickNearestFreeBeacon(Toroidal2DPhysics space, Ship ship) {
		// get the current beacons
		ArrayList<Beacon> beacons = space.getBeacons();

		Beacon closestBeacon = null;
		double bestDistance = Double.POSITIVE_INFINITY;

		for (Beacon beacon : beacons) {
			if (beaconToShipMap.containsKey(beacon)) {
				continue;
			}

			double dist = space.findShortestDistance(ship.getPosition(), beacon.getPosition());
			if (dist < bestDistance) {
				bestDistance = dist;
				closestBeacon = beacon;
			}
		}

		return closestBeacon;
	}




	/**
	 * Clean up data structure including beacon maps
	 */
	public void getMovementEnd(Toroidal2DPhysics space, Set<SpacewarActionableObject> actionableObjects) {

		// once a beacon has been picked up, remove it from the list 
		// of beacons being pursued (so it can be picked up at its
		// new location)
		for (Beacon beacon : space.getBeacons()) {
			if (!beacon.isAlive()) {
				shipToBeaconMap.remove(beaconToShipMap.get(beacon));
				beaconToShipMap.remove(beacon);
			}
		}

	}

	@Override
	public void initialize() {
		beaconToShipMap = new HashMap<Beacon, Ship>();
		shipToBeaconMap = new HashMap<Ship, Beacon>();
	}

	@Override
	public void shutDown() {
		// TODO Auto-generated method stub

	}

	@Override
	public Set<Shadow> getNewShadows() {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public Set<Shadow> getOldShadows() {
		// TODO Auto-generated method stub
		return null;
	}


	@Override
	/**
	 * Beacon collector never purchases
	 */
	public Map<UUID, SpacewarPurchaseEnum> getTeamPurchases(Toroidal2DPhysics space,
			Set<Ship> ships, int availableMoney, int currentCostNewBase) {
		return new HashMap<UUID,SpacewarPurchaseEnum>();
	}


	@Override
	public Map<UUID, SpacewarPowerup> getPowerups(Toroidal2DPhysics space,
			Set<SpacewarActionableObject> actionableObjects) {
		// TODO Auto-generated method stub
		return null;
	}

}
